为什么不能用 innerHTML 清空
最简单的卸载方式:
container.innerHTML = '' // ❌ 简单但有严重问题
javascript
三个核心问题
| 问题 | 说明 |
|---|---|
| 1. 事件未清理 | 只移除了 DOM 节点,addEventListener 绑定的事件监听器未被移除 |
| 2. 生命周期钩子未触发 | beforeUnmount、unmounted 等钩子函数不会执行 |
| 3. 自定义指令未清理 | 挂载在节点上的自定义指令的 unmounted 钩子不会触发 |
正确的卸载方式
记录真实 DOM 引用
在挂载时将真实 DOM 元素记录到 vnode 上:
function mountElement(vnode, container) {
const el = createElement(vnode.type)
// ...
vnode.el = el // 记录真实 DOM 引用
insert(el, container)
}
javascript
卸载函数实现
function unmount(vnode) {
// 获取真实 DOM 节点
const el = vnode.el
const parent = el.parentNode
if (parent) {
parent.removeChild(el)
}
// 可以在这里扩展:
// 1. 触发 beforeUnmount / unmounted 钩子
// 2. 移除事件监听器
// 3. 清理自定义指令
}
javascript
render 方法中使用
function render(vnode, container) {
if (vnode) {
patch(container._vnode, vnode, container)
} else if (container._vnode) {
// 有旧 vnode 但无新 vnode → 卸载
unmount(container._vnode)
}
container._vnode = vnode
}
javascript
渲染器中的卸载时机
卸载发生在以下场景:
render(null, container):显式传入 null 卸载整个容器内容- Diff 算法中:旧节点存在但新节点不存在,需要卸载旧节点
- 节点类型变化:新旧 vnode 的
type不同,卸载旧节点再挂载新节点
function patch(n1, n2, container) {
if (n1 && n1.type !== n2.type) {
// 类型不同:卸载旧的,挂载新的
unmount(n1)
n1 = null
}
// ...
}
javascript
总结
卸载操作的关键点:
- 通过
vnode.el获取真实 DOM 引用,而非使用innerHTML清空 - 在
unmount函数中集中处理清理逻辑:事件移除、生命周期钩子、自定义指令 - 虚拟 DOM 和真实 DOM 的对应关系通过
vnode.el属性建立,这是卸载操作的基础
↑